/**
 * @file Access_interface.h
 * @author Фетисов Михаил Вячеславович (fetisov.michael@yandex.ru)
 * @brief Объявления интерфейса функций оболочки, открытых для плагинов.
 * @version 0.1
 * @date 2023-02-16
 *
 * @copyright MIT: Michael Fetisov, Anton Bushev
 *
 */
#ifndef simodo_shell_Access_interface_H
#define simodo_shell_Access_interface_H

#include <QString>

#include <memory>

class QAction;
class QFileSystemModel;
class QJsonObject;

namespace simodo::module
{
class ModuleCollector_interface;
}

/// \attention PVS Studio: V1061 Extending the 'std' namespace may result in undefined behavior.

namespace std
{
template <typename T>
class optional;
}

namespace simodo::shell
{
    class Plugin_interface;
    class DocumentAdaptor_interface;
    class ViewAdaptor_interface;
    class LspAccess_interface;
    class RunnerManagement_interface;

    inline const QString dataFolderMacros_const {"${dataFolder}"};

/**
 * @brief Степень суровости сообщений.
 *
 */
enum class MessageSeverity
{
    Debug = 0,  ///< Отладочное сообщение
    Info,       ///< Информационное сообщение
    Warning,    ///< Предупреждение (продолжение работы возможно)
    Error,      ///< Ошибка (продолжение работы невозможно)
};

/**
 * @brief Интерфейс подключения к оболочке для получения необходимой информации и
 * выполнения вызовов некоторых функций оболочки.
 *
 */
class Access_interface
{
public:
    virtual ~Access_interface() = default;

    /**
     * @brief Получение доступа к модели файловой системы оболочки.
     *
     * @attention Работать с моделью файловой системы оболочки следует с осторожностью,
     * в первую очередь лучше использовать другие доступные функции оболочки.
     *
     * @return ссылка на модель файловой системы
     */
    virtual QFileSystemModel & filesystem_model() = 0;

    /**
     * @brief Отправка сообщения на панель глобальных сообщений оболочки.
     *
     * @details
     * Если панель сообщений не видима, то сообщения с важностью MessageSeverity::Warning и
     * MessageSeverity::Error сделают панель видимой. Для сообщений уровня MessageSeverity::Info
     * и MessageSeverity::Debug такого выполняться не будет, их можно будет посмотреть пользователю, 
     * вручную открыв панель глобальных сообщений.
     *
     * Все сообщения, выводимые на глобальную панель сообщений дублируются в стандартный вывод
     * операционной системы.
     *
     * @param text текст сообщения,
     * @param severity уровень важности сообщения
     */
    virtual void sendGlobal(const QString & text, MessageSeverity severity=MessageSeverity::Info) = 0;

    /**
     * @brief Открыть документ в оболочке по заданному пути к файлу.
     * Оболочка автоматически определит тип документа, выберет нужный плагин и откроет окно с документом.
     *
     * @note Метод возвращает логический признак ошибки, а не её текст, т.к. за отображение ошибки
     * отвечает оболочка. Никаких действий для оповещения пользователя плагину выполнять не нужно.
     *
     * @param path полный путь к файлу документа
     * @return true - документ загружен и открыт,
     * @return false - документ не удалось загрузить.
     */
    virtual bool openFile(const QString &path) = 0;

    /**
     * @brief Получение константного указателя на объект управления отмены действий пользователя
     * для текущего документа.
     * Обычно этот указатель используется для соединения со слотами и сигналами его объекта из плагина.
     *
     * @return константный указатель на объект управления функцией оболочки
     */
    virtual const QAction * edit_undo_action() const = 0;

    /**
     * @brief Получение константного указателя на объект управления восстановления действий
     * пользователя после отмены для текущего документа.
     * Обычно этот указатель используется для соединения со слотами и сигналами его объекта из плагина.
     *
     * @return константный указатель на объект управления функцией оболочки
     */
    virtual const QAction * edit_redo_action() const = 0;

    /**
     * @brief Получение константного указателя на объект управления вырезания фрагмента в системный
     * буфер обмена для текущего документа.
     * Обычно этот указатель используется для соединения со слотами и сигналами его объекта из плагина.
     *
     * @return константный указатель на объект управления функцией оболочки
     */
    virtual const QAction * cut_action() const = 0;

    /**
     * @brief Получение константного указателя на объект управления копированием фрагмента в системный
     * буфер обмена для текущего документа.
     * Обычно этот указатель используется для соединения со слотами и сигналами его объекта из плагина.
     *
     * @return константный указатель на объект управления функцией оболочки
     */
    virtual const QAction * copy_action() const = 0;

    /**
     * @brief Вывод информации о положении курсора документа в строку состояния оболочки.
     *
     * @param info текст информации
     */
    virtual void displayCursorPosition(const QString & info) = 0;

    /**
     * @brief Вывод информации статистики документа в строку состояния оболочки.
     *
     * @param info текст информации
     */
    virtual void displayStatistics(const QString & info) = 0;

    /**
     * @brief Получение константного указателя на адаптер документа.
     *
     * @details
     * Если нужно получить содержимое какого-либо файла, лучше обратиться к этому методу, т.к. нужный
     * файл может быть загружен в оболочку и изменён пользователем. Пользователь будет ожидать, что
     * этот факт должен быть известен приложению и не сохранить соответствующий документ (или забыть
     * это сделать или ещё по каким-то причинам не выполнить сохранение). Поэтому, важно пользоваться
     * данным методом для получения содержимого нужного файла.
     *
     * Если признак создания нового адаптера (аргумент `create`) выставлен в true, то оболочка сама выполнит
     * процедуру создания адаптера документа. Если создание адаптера документа будет невозможно, оболочка
     * выведет информационное сообщение об ошибке на глобальную панель сообщений и вернёт нулевой
     * указатель.
     *
     * Если признак создания нового адаптера документа не установлен, то оболочка ищет уже загруженные
     * адаптеры. Если таковых нет, то возвращает нулевой указатель.
     *
     * @note Оболочка не уничтожает адаптеры документов сразу при закрытии окна документа. Поэтому, нельзя
     * пользоваться данным методом для определения открыт документ или нет.
     *
     * @param path полный путь к файлу документа,
     * @param create признак, указывающий оболочке, что нужно создать новый адаптер документа, если
     * его нет в коллекции адаптеров оболочки.
     * @return константный указатель на адаптер документа
     */
    virtual const std::shared_ptr<shell::DocumentAdaptor_interface> getDocumentAdaptor(const QString & path, bool create=true) = 0;
    
    virtual std::optional<QString> getDocumentAdaptorPath(const std::shared_ptr<shell::DocumentAdaptor_interface> document_adaptor) = 0;

    /**
     * @brief Получение константного указателя на объект адаптера документа, с которым в данный момент работает
     * пользователь.
     * 
     * Если в оболочке нет открытых документов, то будет возвращён нулевой указатель.
     *
     * @deprecated Данный метод будет удалён из интерфейса из соображений безопасности, пользуйтесь пересылками
     * данных: методы sendDataToPanel и sendDataToCurrentDocument.
     *
     * @return константный указатель на объект адаптера документа
     */
    virtual const ViewAdaptor_interface * getCurrentViewAdaptor() = 0;

    /**
     * @brief Установка признака изменения текущего документа.
     * Этот метод влияет только на отображение звёздочки на вкладке документа в оболочке и никак не затрагивает
     * адаптер документа. Метод упрощает отображение звёздочки на вкладке для плагинов документов.
     *
     * @param modified признак, что документ изменён
     */
    virtual void setCurrentDocumentWindowModified(bool modified) = 0;

    /**
     * @brief Создание панели.
     *
     * @details
     * С помощью данного метода можно создавать только панели, которые не являются уникальными (shell::Singleton)
     * и для которых существует плагин panel_type. В противном случае метод вернёт false.
     *
     * Если панель с заданным наименование есть, то метод не станет создавать новую панель и вернёт true.
     *
     * Данный метод позволяет создавать несколько панелей одного типа для отображения сходной информации.
     * Например, для отображения графиков.
     *
     * @param panel_type тип панели,
     * @param panel_name наименование панели,
     * @param title заголовок панели
     * @return true - если панель создана или существует в оболочке,
     * @return false - в противном случае.
     */
    virtual bool createPanel(const QString & panel_type, const QString & panel_name, const QString & title) = 0;

    /**
     * @brief Сделать заданную панель видимой пользователю
     * 
     * @param panel_name имя панели
     * @return true панель с заданным именем была найдена и сделана видимой
     * @return false панель с заданным именем не найдена
     */
    virtual bool risePanel(const QString & panel_name, const QString &title) = 0;

    /**
     * @brief Изменение заголовка панели
     * 
     * @param panel_name имя панели
     * @param title      заголовок панели
     * @return true      панель с заданным именем найдена и заголовок изменён
     * @return false     панель с заданным именем не найдена
     */
    virtual bool setPanelTitle(const QString & panel_name, const QString & title) = 0;

    /**
     * @brief Отправка данных для указанной панели.
     *
     * @details
     * Данные для панели передаются в структуре JSON (класс QJsonObject). Конкретная структура определяется
     * реализацией конкретной панели.
     *
     * @param panel_name уникальное имя панели,
     * @param value данные
     * @return true
     * @return false
     */
    virtual bool sendDataToPanel(const QString & panel_name, const QString &title, const QJsonObject & value) = 0;

    /**
     * @brief Отправка данных для текущего документа.
     *
     * @details
     * Данные передаются в структуре JSON (класс QJsonObject). Конкретная структура определяется
     * реализацией конкретного адаптера представления документа.
     *
     * @param value данные
     * @return true - данные успешно обработаны,
     * @return false - текущего документа нет (все документы в оболочке закрыты) или произошла ошибка обработки данных
     */
    virtual bool sendDataToCurrentDocument(const QJsonObject & value) = 0;

    /**
     * @brief Запрос пути к папке для хранения данных указанного плагина.
     *
     * @details
     * Оболочка определяет путь к папке для указанного плагина. Если папка по этому пути не существует, то
     * она создаётся.
     *
     * Другие плагины не будут иметь доступ к предоставленной папке.
     *
     * @param plugin плагин, запрашивающий путь к папке хранения данных
     * @return путь к папке хранения данных для заданного плагина
     */
    virtual QString provideDataStorageFolder(const Plugin_interface * plugin) = 0;

    /**
     * @brief Запрос пути к папке для хранения данных для заданной функциональности.
     *
     * @details
     * Оболочка определяет путь к папке для указанной функциональности. Если папка по этому пути не существует, то
     * она создаётся.
     *
     * В отличии от получения папки по заданному плагину, предполагается, что функциональность
     * может быть общей для нескольких плагинов.
     *
     * @param feature_name наименование функциональности.
     * @return путь к папке хранения данных для заданной функциональности
     */
    virtual QString provideDataStorageFolder(const QString & feature_name) = 0;

    /**
     * @brief Заменяет все внутренние макросы на их текущие значения.
     * 
     * @param text исходная строка с макросами.
     * @return строка с подстановленными значениями 
    */
    virtual QString performAllMacroSubstitutions(const QString & text) = 0;

    /**
     * @brief Предоставляет указатель на интерфейс коллектора модулей.
     *
     * @return указатель на интерфейс коллектора модулей
     */
    virtual module::ModuleCollector_interface * module_collector() = 0;

    /**
     * @brief Получение доступа к функциям LSP из плагинов
     *
     * Если передать имя файла оболочка проверит поддержку данного имени (расширения),
     * если нет серверов, которые поддерживают такие файлы, то вернёт nullptr.
     *
     * @param path_to_file полный путь к файлу или пустая строка
     * @return Интерфейс доступа к функциям LSP из плагинов
     */
    virtual LspAccess_interface * getLspAccess(const QString & path_to_file) = 0;

    /**
     * @brief Получение доступа к управлению моделированием
     * 
     * @return Интерфейс доступа к управлению моделированием
    */
    virtual RunnerManagement_interface * getRunnerManagement() = 0;

    /**
     * @brief Передача в оболочку, что документ файла path_to_file изменил содержимое
     * 
     * Эта нотификация нужна для автоматического запуска связанного ранера.
    */
    virtual void notifyContentChanged(const QString & path_to_file) = 0;

};

}

#endif // simodo_shell_Access_interface_H
